
import cantools
import os

class dbcParse_SignalsPbconfigGenerate:
    def __init__(self):
        pass

    # 判断num是否为浮点数
    def __is_float(self, num):  
        return isinstance(num, float)

    # 将一个全0数的前num位置1
    def __set_first_n_bits_to_one(self, num):  
        if num > 64:  
            raise ValueError("Cannot set more than 64 bits to 1")  

        # 创建一个掩码，其中前num位为1，其余位为0  
        mask = (1 << num) - 1        

        return mask

    # 计算取值范围，根据取值范围返回类型名
    def __charge_int_signal_val_range(self, length, scale, offset, is_signed):
        param_typename = ''

        if(is_signed):
            param_typename = 'signed int'
        else: 
            minVal = 0*scale + offset
            maxVal = self.__set_first_n_bits_to_one(length)*scale + offset
            if(minVal<0):
                param_typename = 'signed int'
            elif((minVal>=0) and (maxVal<=255)):
                param_typename = 'uint8'
            elif ((minVal>=0) and (maxVal<=65535)):
                param_typename = 'uint16'
            elif ((minVal>=0) and (maxVal<=4294967295)):
                param_typename = 'uint32'
            else:
                param_typename = 'uint64'

        return param_typename

    # 创建信号配置文件
    def __create_pbcfg_c_file(self, dbc_file_pathname, dbc_ecu_name):
        dbc_info = cantools.db.load_file(dbc_file_pathname)
        dbc_c_file_name = dbc_file_pathname.replace('.dbc', '_SignalsPbconfig.c')
        file_folder = 'src_generate'
        if not os.path.exists(file_folder):
            os.mkdir(file_folder)
        dbc_c_file_pathname = os.path.join(file_folder, dbc_c_file_name)
        # 遍历报文及信号
        with open(dbc_c_file_pathname, 'w', encoding='utf-8') as file_c:
            line_chars = '#include \"' + dbc_file_pathname.replace('.dbc', '_SignalsPbconfig.h') + '\"'
            file_c.write(line_chars + '\n\n')

            # 生成signal的配置结构体
            for message in dbc_info.messages:
                for signal in message.signals:
                    line_chars = 'const tCANSIGNALCONFIG ' + dbc_file_pathname.replace('.dbc', '_') + message.name + '_' + signal.name + '_PbCfg = {'
                    file_c.write(line_chars + '\n')

                    # 信号成员变量
                    # 1）信号物理值的数据类型 dataType
                    sigPhyType = ''
                    if(self.__is_float(signal.scale)):
                        sigPhyType = 'SIGPHYDATATYPE_FLOAT'    # 浮点型
                    else:
                        sigPhyType = self.__charge_int_signal_val_range(signal.length, signal.scale, signal.offset, signal.is_signed)    # 整型
                    
                    if(sigPhyType == 'signed int'):
                        sigPhyType = 'SIGPHYDATATYPE_SINT64'
                    elif(sigPhyType == 'uint8'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT8'
                    elif(sigPhyType == 'uint16'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT16'
                    elif(sigPhyType == 'uint32'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT32'
                    elif(sigPhyType == 'uint64'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT64'

                    line_chars = '    .dataType = ' + sigPhyType + ',' + '    /* 信号物理值数据类型 */'
                    file_c.write(line_chars + '\n')

                    # 2）信号原始值数据类型 rawDataType
                    sigRawType = ''
                    if(signal.is_signed):
                        sigRawType = 'SIGRAWDATATYPE_SIGNED'
                    else:
                        sigRawType = 'SIGRAWDATATYPE_UNSIGNED'
                    line_chars = '    .rawDataType = ' + sigRawType + ',' + '    /* 信号在CAN报文中的原始数据类型: signed/unsigned */'
                    file_c.write(line_chars + '\n')

                    # 3）信号数据大小端 dataEndian
                    sigEndian = ''
                    if(signal.byte_order == 'big_endian'):
                        sigEndian = 'SIGBYTEENDIAN_MOTOROLAR'
                    else:
                        sigEndian = 'SIGBYTEENDIAN_INTEL'
                    
                    line_chars = '    .dataEndian = ' + sigEndian + ',' + '    /* 信号数据格式：motorolar/intel */'
                    file_c.write(line_chars + '\n')

                    # 4）分辨率数据类型 factorDataType
                    sigFactorDataType = ''
                    if(self.__is_float(signal.scale)):
                        sigFactorDataType = 'SIGFACTOROFFSETDATATYPE_FLOAT'
                    elif(signal.scale >= 0):
                        sigFactorDataType = 'SIGFACTOROFFSETDATATYPE_INTPOSITIVE'
                    else:
                        sigFactorDataType = 'SIGFACTOROFFSETDATATYPE_INTNEGATIVE'
                    line_chars = '    .factorDataType = ' + sigFactorDataType + ',' + '    /* 分辨率数据类型 */'
                    file_c.write(line_chars + '\n')

                    # 5）分辨率数值 factorVal
                    line_chars = '    .factorVal = ' + str(signal.scale) + ',' + '    /* 分辨率数据，存储为float64格式 */'
                    file_c.write(line_chars + '\n')

                    # 6）偏移量数据类型 offsetDataType
                    sigOffsetDataType = ''
                    if(self.__is_float(signal.offset)):
                        sigOffsetDataType = 'SIGFACTOROFFSETDATATYPE_FLOAT'
                    elif(signal.offset >= 0):
                        sigOffsetDataType = 'SIGFACTOROFFSETDATATYPE_INTPOSITIVE'
                    else:
                        sigOffsetDataType = 'SIGFACTOROFFSETDATATYPE_INTNEGATIVE'
                    line_chars = '    .offsetDataType = ' + sigOffsetDataType + ',' + '    /* 偏移量数据类型 */'
                    file_c.write(line_chars + '\n')

                    # 7）偏移量数值 offsetVal
                    line_chars = '    .offsetVal = ' + str(signal.offset) + ',' + '    /* 偏移量数据，存储为float64格式 */'
                    file_c.write(line_chars + '\n')

                    # 8）信号起始位数值 startBit
                    line_chars = '    .startBit = ' + str(signal.start) + 'u,' + '    /* 信号起始位 */'
                    file_c.write(line_chars + '\n')

                    # 9）信号长度数值 lengthBits
                    line_chars = '    .lengthBits = ' + str(signal.length) + 'u,' + '    /* 信号长度 */'
                    file_c.write(line_chars + '\n')

                    # 10）信号复用类型 isMultiplexor
                    line_chars = '    .isMultiplexor = ' + str(signal.is_multiplexer) + ',' + '    /* 信号复用 */'
                    file_c.write(line_chars + '\n')

                    # 11） maximum
                    if signal.maximum:
                        line_chars = '    .maxminumValid = ' + 'True,' + '    /* 信号最大值有效性 */'  
                        file_c.write(line_chars + '\n')
                        line_chars = '    .maximum = ' + str(signal.maximum) + ',' + '    /* 信号最大值 */'
                        file_c.write(line_chars + '\n')
                    else:
                        line_chars = '    .maxminumValid = ' + 'False,' + '    /* 信号最大值有效性 */'  
                        file_c.write(line_chars + '\n')
                    
                    # 12） minimum
                    if signal.minimum:
                        line_chars = '    .minimumValid = ' + 'True,' + '    /* 信号最小值有效性 */'  
                        file_c.write(line_chars + '\n')
                        line_chars = '    .minimum = ' + str(signal.minimum) + ',' + '    /* 信号最小值 */'
                        file_c.write(line_chars + '\n')
                    else:
                        line_chars = '    .minimumValid = ' + 'False,' + '    /* 信号最小值有效性 */'  
                        file_c.write(line_chars + '\n')

                    # 结构体结尾大括号
                    line_chars = '};'
                    file_c.write(line_chars + '\n\n')
            
            # 生成message的结构体定义
            for message in dbc_info.messages:
                dbc_name = dbc_file_pathname.replace('.dbc', '')
                # line_chars = dbc_name + '_' + message.name.upper() + '_STRUCT ' + dbc_name + '_' + message.name + '=\n{'
                line_chars = dbc_name + '_' + message.name.upper() + '_STRUCT t' + dbc_name + '_' + message.name +  '=\n{'
                file_c.write(line_chars + '\n')

                for signal in message.signals:
                    line_chars = '    .sig_' + signal.name + '_Val = 0,'
                    file_c.write(line_chars + '\n')

                line_chars = '};'
                file_c.write(line_chars + '\n\n')

                # line_chars = dbc_name + '_' + message.name.upper() + '_STRUCT t' + message.name + ';'
                # file_c.write(line_chars + '\n\n')


    def __create_pbcfg_h_file(self, dbc_file_pathname, dbc_ecu_name):
        dbc_info = cantools.db.load_file(dbc_file_pathname)
        dbc_h_file_name = dbc_file_pathname.replace('.dbc', '_SignalsPbconfig.h')
        file_folder = 'src_generate'
        if not os.path.exists(file_folder):
            os.mkdir(file_folder)
        dbc_h_file_pathname = os.path.join(file_folder, dbc_h_file_name)
        # 遍历报文及信号
        with open(dbc_h_file_pathname, 'w', encoding='utf-8') as file_h:
            line_chars = '#ifndef ' + str((dbc_h_file_name.replace('.h', '_H'))).upper()
            file_h.write(line_chars + '\n')
            line_chars = "#define " + str((dbc_h_file_name.replace('.h', '_H'))).upper()
            file_h.write(line_chars + '\n\n')
            
            line_chars = '#include \"dbcParse_PublicDefine.h\"'
            file_h.write(line_chars + '\n\n')

            for message in dbc_info.messages:
                for signal in message.signals:
                    line_chars = 'extern const tCANSIGNALCONFIG ' + dbc_file_pathname.replace('.dbc', '_') + message.name + '_' + signal.name + '_PbCfg;'
                    file_h.write(line_chars + '\n')
                file_h.write('\n')

            
            # 生成message的结构体定义
            for message in dbc_info.messages:
                line_chars = 'typedef struct\n{'
                file_h.write(line_chars + '\n')

                for signal in message.signals:
                    # 信号物理值的数据类型 dataType
                    sigPhyType = ''
                    if(self.__is_float(signal.scale)):
                        sigPhyType = 'SIGPHYDATATYPE_FLOAT'    # 浮点型
                    else:
                        sigPhyType = self.__charge_int_signal_val_range(signal.length, signal.scale, signal.offset, signal.is_signed)    # 整型
                    
                    if(sigPhyType == 'signed int'):
                        sigPhyType = 'SIGPHYDATATYPE_SINT64'
                    elif(sigPhyType == 'uint8'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT8'
                    elif(sigPhyType == 'uint16'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT16'
                    elif(sigPhyType == 'uint32'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT32'
                    elif(sigPhyType == 'uint64'):
                        sigPhyType = 'SIGPHYDATATYPE_UINT64'

                    if(sigPhyType == 'SIGPHYDATATYPE_FLOAT'):
                        line_chars = '    double sig_' + signal.name + '_Val;'
                        file_h.write(line_chars + '\n')
                    elif(sigPhyType == 'SIGPHYDATATYPE_SINT64'):
                        line_chars = '    sint64 sig_' + signal.name + '_Val;'
                        file_h.write(line_chars + '\n')
                    elif(sigPhyType == 'SIGPHYDATATYPE_UINT8'):
                        line_chars = '    uint8 sig_' + signal.name + '_Val;'
                        file_h.write(line_chars + '\n')
                    elif(sigPhyType == 'SIGPHYDATATYPE_UINT16'):
                        line_chars = '    uint16 sig_' + signal.name + '_Val;'
                        file_h.write(line_chars + '\n')
                    elif(sigPhyType == 'SIGPHYDATATYPE_UINT32'):
                        line_chars = '    uint32 sig_' + signal.name + '_Val;'
                        file_h.write(line_chars + '\n')
                    elif(sigPhyType == 'SIGPHYDATATYPE_UINT64'):
                        line_chars = '    uint64 sig_' + signal.name + '_Val;'
                        file_h.write(line_chars + '\n')
                
                dbc_name = dbc_file_pathname.replace('.dbc', '')
                line_chars = '} ' + dbc_name + '_' + message.name.upper() + '_STRUCT;'
                file_h.write(line_chars + '\n')

                line_chars = 'extern ' + dbc_name + '_' + message.name.upper() + '_STRUCT t' + dbc_name + '_' + message.name + ';'
                file_h.write(line_chars + '\n\n')

            line_chars = '#endif'
            file_h.write(line_chars + '\n')


    def create_pbcfg_files(self, dbc_file_pathname, dbc_ecu_name):
        self.__create_pbcfg_c_file(dbc_file_pathname, dbc_ecu_name)
        self.__create_pbcfg_h_file(dbc_file_pathname, dbc_ecu_name)

